9.2. XML

正如您可能已经知道,Jersey 使用 MessageBodyWriter 和MessageBodyReader 年代来解析传入的请求和创建传出的响应。每个用户都可以创建 自己的表现但是…这是不建议这样做。XML 是证明交换信息的标准,特别是在web 服务。Jersey 支持低水平数据类型用于直接操作和JAXB XML 实体。

9.2.1. Low level XML support 低级 XML 支持

Jersey 目前支持一些低水平的数据类型:StreamSource, SAXSource, DOMSourceDocument。您可以使用这些类型的返回类型或方法(资源)参数。让说我们想要测试这个功能,我们有 helloworld示例 作为起点。所有我们需要做的就是添加方法(资源)的消耗和产生的 XML 和类型将使用上面提到的。

Example 8.31. Low level XML test - methods added to HelloWorldResource.java

  1. @POST
  2. @Path("StreamSource")
  3. public StreamSource getStreamSource(StreamSource streamSource) {
  4. return streamSource;
  5. }
  6. @POST
  7. @Path("SAXSource")
  8. public SAXSource getSAXSource(SAXSource saxSource) {
  9. return saxSource;
  10. }
  11. @POST
  12. @Path("DOMSource")
  13. public DOMSource getDOMSource(DOMSource domSource) {
  14. return domSource;
  15. }
  16. @POST
  17. @Path("Document")
  18. public Document getDocument(Document document) {
  19. return document;
  20. }

MessageBodyWriter 和 MessageBodyReader 都在这个例子中使用了,我们需要的是一个 POST 请求 XML 文档作为一个请求的实体。让这只尽可能简单的根元素没有内容将发送:“”。您可以创建 JAX-RS 客户端,或使用其他一些工具,例如 curl:

curl -v http://localhost:8080/base/helloworld/StreamSource -d “

你应该从我们的服务得到完全相同的 XML ;在本例中,XML 头添加到响应但内容停留。自由的遍历所有资源。

9.2.2. Getting started with JAXB 开始

好的开始,人们已经有了一些 JAXB 注解的经验,例子是是 JAXB示例。你可以看到不同的用例。本文主要是针对那些没有 JAXB 经验。别指望所有可能的注释和他们的组合将在这一章,JAXB(JSR 222实现)是相当复杂和全面。但如果你只是想知道如何与 REST 服务交换 XML 消息,你看着合适的章节。

可以从简单的例子开始。让我们说我们有类 Planet 和服务生产的“Planets”。

Example 9.32. Planet class

  1. @XmlRootElement
  2. public class Planet {
  3. public int id;
  4. public String name;
  5. public double radius;
  6. }

Example 9.33. Resource class

  1. @Path("planet")
  2. public class Resource {
  3. @GET
  4. @Produces(MediaType.APPLICATION_XML)
  5. public Planet getPlanet() {
  6. final Planet planet = new Planet();
  7. planet.id = 1;
  8. planet.name = "Earth";
  9. planet.radius = 1.0;
  10. return planet;
  11. }
  12. }

你可以看到有一些额外的注释声明类 Planet,尤其是@XmlRootElement。这是一个 JAXB 注释的 java 类映射到 XML 元素。我们不需要指定任何其他,因为 Planet 非常简单的类,所有的字段都是公开的。在这种情况下,XML 元素名称将派生类名或你可以设置名称属性:@XmlRootElement(name=”yourName”)。

我们的资源类将响应 GET/planet 请求

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2. <planet>
  3. <id>1</id>
  4. <name>Earth</name>
  5. <radius>1.0</radius>
  6. </planet>

这可能正是我们想要的……与否。或者我们可能不关心,因为我们可以使用 JAX-RS 客户端发出请求该资源,这很容易:

  1. Planet planet = webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(Planet.class);

有预先创建 WebTarget 对象指向我们的应用程序的上下文根,只需添加路径(在我们的例子中是planet),接收 header(不是强制性的,但服务可以提供不同的内容基于这头,例如可以为 text/html 在 web 浏览器),最后我们指定,我们预计 Planet 类通过GET请求。

不仅可能需要生成 XML ,我们可能希望使用它。

Example 9.34. Method for consuming Planet

  1. @POST
  2. @Consumes(MediaType.APPLICATION_XML)
  3. public void setPlanet(Planet planet) {
  4. System.out.println("setPlanet " + planet);
  5. }

有效的请求后,服务将打印字符串表示的 Planet,可以像 Planet{id=2, name=’Mars’, radius=1.51}。通过 JAX-RS 客户端你能做到:

  1. webTarget.path("planet").post(planet);

如果有需要其他(非默认的) XML 表示,其他 JAXB 注解需要被使用。简化这一过程通常是由从 XML 模式生成 java 源代码是通过 XML 到 java 编译器和它的 xjc 是 JAXB 的一部分。

9.2.3. POJOs

有时,你不能或者不想在代码里面使用注解,但又想用消费和生成 XML 的类的表现形式。这种情况下就可以用 JAXBElement 。下面例子就是没有用 @XmlRootElement 注解:

Example 9.35. Resource class - JAXBElement

  1. @Path("planet")
  2. public class Resource {
  3. @GET
  4. @Produces(MediaType.APPLICATION_XML)
  5. public JAXBElement<Planet> getPlanet() {
  6. Planet planet = new Planet();
  7. planet.id = 1;
  8. planet.name = "Earth";
  9. planet.radius = 1.0;
  10. return new JAXBElement<Planet>(new QName("planet"), Planet.class, planet);
  11. }
  12. @POST
  13. @Consumes(MediaType.APPLICATION_XML)
  14. public void setPlanet(JAXBElement<Planet> planet) {
  15. System.out.println("setPlanet " + planet.getValue());
  16. }
  17. }

正如您可以看到的,一切都是用了 JAXBElement 就会复杂一些。这是因为现在需要显式地设置元素名称的给 Planet 类的 XML 表示。客户端比服务器端更加复杂,因为你不能做 JAXBElement<Planet> 所以 JAX-RS 客户端 API 提供了如何通过 声明 GenericType<T> 的子类 解决它

Example 9.36. Client side - JAXBElement

  1. // GET
  2. GenericType<JAXBElement<Planet>> planetType = new GenericType<JAXBElement<Planet>>() {};
  3. Planet planet = (Planet) webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(planetType).getValue();
  4. System.out.println("### " + planet);
  5. // POST
  6. planet = new Planet();
  7. // ...
  8. webTarget.path("planet").post(new JAXBElement<Planet>(new QName("planet"), Planet.class, planet));

9.2.4. Using custom JAXBContext 使用自定义 JAXBContext

有些场景适合使用自定义 JAXBContext。JAXBContext 的创建是一个昂贵的操作,如果你已经创建了一个,相同的实例被 Jersey 使用。其他可能使用的情况是当你需要给 JAXBContext 建立一些特定的东西,例如设置不同的类装载器。

Example 9.37. PlanetJAXBContextProvider

  1. @Provider
  2. public class PlanetJAXBContextProvider implements ContextResolver<JAXBContext> {
  3. private JAXBContext context = null;
  4. public JAXBContext getContext(Class<?> type) {
  5. if (type != Planet.class) {
  6. return null; // we don't support nothing else than Planet
  7. }
  8. if (context == null) {
  9. try {
  10. context = JAXBContext.newInstance(Planet.class);
  11. } catch (JAXBException e) {
  12. // log warning/error; null will be returned which indicates that this
  13. // provider won't/can't be used.
  14. }
  15. }
  16. return context;
  17. }
  18. }

上面示例简单创建 JAXBContext 的过程,所有你需要做的就是把这个@Provider 注释放上,这样 Jersey 就能找到它。用户有时在客户端使用 provider (提供者)类 出问题,所以只是为了提醒——你必须 在客户端配置(客户端做任何事情不像通过服务器包扫描)声明他们。

Example 9.38. Using Provider with JAX-RS client

  1. ClientConfig config = new ClientConfig();
  2. config.register(PlanetJAXBContextProvider.class);
  3. Client client = ClientBuilder.newClient(config);

9.2.5. MOXy

如果你想使用 MOXy 作为 JAXB 实现而不是 JAXB RI 您有两种选择。您可以使用标准的 JAXB 机制来定义 从 JAXBContext 实例将获得(有关此主题的更多信息,读JavaDoc JAXBContext)的 JAXBContextFactory 或者你可以将 jersey-media-moxy 模块添加到您的项目和注册/配置 MoxyXmlFeature 类/实例的Configurable

Example 9.39. Add jersey-media-moxy dependency.

  1. <dependency>
  2. <groupId>org.glassfish.jersey.media</groupId>
  3. <artifactId>jersey-media-moxy</artifactId>
  4. <version>2.16</version>
  5. </dependency>

Example 9.40. Register the MoxyXmlFeature class.

  1. final ResourceConfig config = new ResourceConfig()
  2. .packages("org.glassfish.jersey.examples.xmlmoxy")
  3. .register(MoxyXmlFeature.class);

Example 9.41. Configure and register an MoxyXmlFeature instance.

  1. // Configure Properties.
  2. final Map<String, Object> properties = new HashMap<String, Object>();
  3. // ...
  4. // Obtain a ClassLoader you want to use.
  5. final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  6. final ResourceConfig config = new ResourceConfig()
  7. .packages("org.glassfish.jersey.examples.xmlmoxy")
  8. .register(new MoxyXmlFeature(
  9. properties,
  10. classLoader,
  11. true, // Flag to determine whether eclipselink-oxm.xml file should be used for lookup.
  12. CustomClassA.class, CustomClassB.class // Classes to be bound.
  13. ));